/*
 * Duplication.c
 * Behavioral Attributed Automaton
 * @author Gregoire JACOB (gregoire.jacob@orange-ftgroup.com)
 * @date 01/12/2008
 * @version 2.1
 * Duplication parser automaton
 */

#include ".\Includes\BehaviorAutomata.h"


void initDuplicationAutomata(){
	int start[3] = {0,0,0};
	long lstart[3] = {0,0,0};
	duplication.automata = NULL;
	duplication.nbnautomata = 0;
	duplication.nbmaxautomata = 0;
	startAutomaton(&duplication,'s',lstart,start);
}


void parseDupCreate(long argid, int argtype){
	char curnode;
	long curids[3]; int curtypes[3];
	long nextids[3] = {0,0,0}; int nextypes[3] = {0,0,0};
	int i, nb = duplication.nbnautomata;

	for(i=0;i<nb;i++){
		struct PARSED_AUTOMATON * aut = &duplication.automata[i];
		//Recover the state of the automaton
		curnode = getCurrentState(aut);
		getCurrentAttributes(aut,curids,curtypes);
		switch(curnode){
			
			case 's': //Initial creation
				if(DupObjectCombin[argid][argid]==1) break;
				//Checks semantic rules
				if(argtype==TYPE_PERM){
					//Create a waiting new automaton replacing the parsed one
					startAutomaton(&duplication,'s',nextids,nextypes);
					nbambiguous++;
					aut = &duplication.automata[i]; //Update relocation
					//Progression toward the next node
					nextids[0] = argid;
					nextypes[0] = argtype;
					addNode(aut,'a',nextids,nextypes);
					DupObjectCombin[argid][argid] = 1;
					//printf("[+] Reach a by create\n");
				}
				break;

			case 'b': //Already open file
				if(argid==curids[0]) break;
				if(DupObjectCombin[curids[0]][argid]==1) break;
				//Checks semantic rules
				if(argtype==TYPE_PERM &&
					curtypes[0]==TYPE_THIS){
					//Create a waiting new automaton replacing the parsed one
					startAutomaton(&duplication,'b',curids,curtypes);
					nbambiguous++;
					aut = &duplication.automata[i]; //Update relocation
					//Progression toward the next node
					curids[1] = argid;
					curtypes[1] = argtype;
					addNode(aut,'c',curids,curtypes);
					DupObjectCombin[curids[0]][argid] = 1;
					//printf("[+] Reach c by open/create \n");
				}
				break;

			case 'm': //Already open and read file (this)
				if(argid==curids[0]) break;
				//Checks semantic rules
				if(argtype==TYPE_PERM){
					//Create a waiting new automaton replacing the parsed one
					startAutomaton(&duplication,'m',curids,curtypes);
					nbambiguous++;
					aut = &duplication.automata[i]; //Update relocation
					//Progression toward the next node
					curids[1] = argid;
					curtypes[1] = argtype;
					addNode(aut,'d',curids,curtypes);
					if(!DupObjectCombin[curids[0]][argid]){
						curids[2] = 0;
						curtypes[2] = 0;
						startAutomaton(&duplication,'c',curids,curtypes);
					}
					DupObjectCombin[curids[0]][argid] = 1;
					//printf("[+] Reach d by open \n");
				}
				break;
		}//swithch
	}//for
}


void parseDupOpen(long argid, int argtype){
	char curnode;
	long curids[3]; int curtypes[3];
	long nextids[3] = {0,0,0}; int nextypes[3] = {0,0,0};
	int i, nb = duplication.nbnautomata;

	for(i=0;i<nb;i++){
		struct PARSED_AUTOMATON * aut = &duplication.automata[i];
		//Recover the state of the automaton
		curnode = getCurrentState(aut);
		getCurrentAttributes(aut,curids,curtypes);
		switch(curnode){
			
			case 's': //Initial open
				if(DupObjectCombin[argid][argid]==1) break;
				//Checks semantic rules
				if(argtype==TYPE_PERM || argtype==TYPE_THIS){
					//Create a waiting new automaton replacing the parsed one
					startAutomaton(&duplication,'s',nextids,nextypes);
					nbambiguous++;
					aut = &duplication.automata[i]; //Update relocation
					//Progression toward the next node
					nextids[0] = argid;
					nextypes[0] = argtype;
					addNode(aut,'b',nextids,nextypes);
					DupObjectCombin[argid][argid] = 1;
					//printf("[+] Reach b by open type: %d (this:5-perm:2)\n",argtype);
				}
				break;

			case 'a': //Already created file
				if(argid==curids[0]) break;
				if(DupObjectCombin[curids[0]][argid]==1) break;
				//Checks semantic rules
				if(argtype==TYPE_THIS||isDuplicata(argid,argtype)){
					//Create a waiting new automaton replacing the parsed one
					startAutomaton(&duplication,'a',curids,curtypes);
					nbambiguous++;
					aut = &duplication.automata[i]; //Update relocation
					//Progression toward the next node
					curids[1] = argid;
					curtypes[1] = argtype;
					addNode(aut,'c',curids,curtypes);
					DupObjectCombin[curids[0]][argid] = 1;
					//printf("[+] Reach c by creat/open \n");
				}
				break;

			case 'b': //Already open file (this or permanent)
				if(argid==curids[0]) break;
				if(DupObjectCombin[curids[0]][argid]==1) break;
				//Checks semantic rules
				if((argtype==TYPE_PERM && 
					(curtypes[0]==TYPE_THIS||isDuplicata(curids[0],curtypes[0])))
					|| ((argtype==TYPE_THIS ||isDuplicata(argid,argtype))
					&& curtypes[0]==TYPE_PERM)){
					//Create a waiting new automaton replacing the parsed one
					startAutomaton(&duplication,'b',curids,curtypes);
					nbambiguous++;
					aut = &duplication.automata[i]; //Update relocation
					//Progression toward the next node
					curids[1] = argid;
					curtypes[1] = argtype;
					addNode(aut,'c',curids,curtypes);
					DupObjectCombin[curids[0]][argid] = 1;
					//printf("[+] Reach c by open/open \n");
				}
				break;

			case 'm': //Already open and read file (this)
				if(argid==curids[0]) break;
				//Checks semantic rules
				if(argtype==TYPE_PERM){
					//Create a waiting new automaton replacing the parsed one
					startAutomaton(&duplication,'m',curids,curtypes);
					nbambiguous++;
					aut = &duplication.automata[i]; //Update relocation
					//Progression toward the next node
					curids[1] = argid;
					curtypes[1] = argtype;
					addNode(aut,'d',curids,curtypes);
					if(!DupObjectCombin[curids[0]][argid]){
						curids[2] = 0;
						curtypes[2] = 0;
						startAutomaton(&duplication,'c',curids,curtypes);
					}
					DupObjectCombin[curids[0]][argid] = 1;
					//printf("[+] Reach d by open \n");
				}
				break;
		}//swithch
	}//for
}

void parseDupClose(long argid, int argtype){
	char curnode;
	long curids[3]; int curtypes[3];
	int i, nb = duplication.nbnautomata;

	for(i=0;i<nb;i++){
		struct PARSED_AUTOMATON * aut = &duplication.automata[i];
		//Recover the state of the automaton
		curnode = getCurrentState(aut);
		getCurrentAttributes(aut,curids,curtypes);
		switch(curnode){
			
			case 'a': //Initial open or create
			case 'b':
				if(argid==curids[0]&&argtype==curtypes[0]){
					//Object is close before any duplication-related operation
					deleteAutomaton(&duplication,i);
					i--; nb--;
					DupObjectCombin[argid][argid] = 0;
				}
				break;

			case 'c':				
				if(argid==curids[0]&&argtype==curtypes[0]){
					//Object is close before any duplication-related operation
					deleteAutomaton(&duplication,i);
					i--; nb--;
					//DupObjectCombin[curids[1]][argid] = 0;//additional
					DupObjectCombin[argid][curids[1]] = 0;
				}else if(argid==curids[1]&&argtype==curtypes[1]){
					//Object is close before any duplication-related operation
					deleteAutomaton(&duplication,i);
					i--; nb--;
					//DupObjectCombin[argid][curids[0]] = 0;//additional
					DupObjectCombin[curids[0]][argid] = 0;
				}
				break;
		}//swithch
	}//for
}

void parseDupRead(long arg1id, int arg1type, long arg2id, int arg2type){
	char curnode;
	long curids[3]; int curtypes[3];
	int i, nb = duplication.nbnautomata;

	for(i=0;i<nb;i++){
		struct PARSED_AUTOMATON * aut = &duplication.automata[i];
		//Recover the state of the automaton
		curnode = getCurrentState(aut);
		getCurrentAttributes(aut,curids,curtypes);
		switch(curnode){
			
			case 'b': //Already open file (this)
				//Checks semantic rules
				if(arg1type==TYPE_VAR && arg2id==curids[0] &&
					(arg2type==TYPE_THIS||isDuplicata(arg2id,arg2type))){
					//Create a waiting new automaton replacing the parsed one
					startAutomaton(&duplication,'b',curids,curtypes);
					nbambiguous++;
					aut = &duplication.automata[i]; //Update relocation
					//Progression toward the next node
					curids[2] = arg1id;
					curtypes[2] = arg1type;
					addNode(aut,'m',curids,curtypes);
					//printf("[+] Reach m by read \n");
				}
				break;

			case 'c': //Simple read
				//Checks semantic rules
				if(arg1type==TYPE_VAR && 
					(arg2type==TYPE_THIS||isDuplicata(arg2id,arg2type)) &&
					(arg2id==curids[0] || arg2id==curids[1])){
					//Create a waiting new automaton replacing the parsed one
					startAutomaton(&duplication,'c',curids,curtypes);
					nbambiguous++;
					aut = &duplication.automata[i]; //Update relocation
					//Progression toward the next node
					if(arg2id==curids[1]){ //Reordering objects
						curids[1] = curids[0];
						curtypes[1] = curtypes[0];
						curids[0] = arg2id;
						curtypes[0] = arg2type;
					}
					curids[2] = arg1id;
					curtypes[2] = arg1type;
					addNode(aut,'d',curids,curtypes);
					//printf("[+] Reach d by read\n");
				}
				break;

			case 'i': //Interleaved read
				//Checks semantic rules
				if(arg1type==TYPE_VAR && 
					(arg2type==TYPE_THIS||isDuplicata(arg2id,arg2type)) &&
					(arg2id==curids[0] || arg2id==curids[1])){
					//Create a waiting new automaton replacing the parsed one
					startAutomaton(&duplication,'i',curids,curtypes);
					nbambiguous++;
					aut = &duplication.automata[i]; //Update relocation			
					//Progression toward the next node
					if(arg2id==curids[1]){ //Reordering objects
						curids[1] = curids[0];
						curtypes[1] = curtypes[0];
						curids[0] = arg2id;
						curtypes[0] = arg2type;
					}
					curids[2] = arg1id;
					curtypes[2] = arg1type;
					addNode(aut,'j',curids,curtypes);
					//printf("[+] Reach j by read\n");
				}
				break;

			/*case 'j': //Overwriting a read value
			case 'd':
				if(arg1id==curids[2]&&arg1type==curtypes[2]){
					//Variable is overwritten before any duplication-related operation
					setCurrentState('e',aut); //Error state (no more progression)
				}
				break;*/
		}//swithch
	}//for
}

void parseDupWrite(long arg1id, int arg1type, long arg2id, int arg2type){
	char curnode;
	long curids[3]; int curtypes[3];
	int final[3] = {0,0,0}; long lfinal[3] = {0,0,0};
	int i, nb = duplication.nbnautomata;

	for(i=0;i<nb;i++){
		struct PARSED_AUTOMATON * aut = &duplication.automata[i];
		//Recover the state of the automaton
		curnode = getCurrentState(aut);
		getCurrentAttributes(aut,curids,curtypes);
		switch(curnode){
			
			case 'd': //Simple write
				//Checks semantic rules
				if(arg1type==TYPE_VAR && arg1id==curids[2] &&
					arg2type==TYPE_PERM && arg2id==curids[1]){
					//Progression toward the next node
					addNode(aut,'f',lfinal,final);
					printf("[+] Simple duplication has been detected "
						"between %s%d and %s%d through %s%d\n",typestr[curtypes[0]],
						curids[0],typestr[curtypes[1]],curids[1],typestr[curtypes[2]],curids[2]);
					storeBehaviorEntry(pf_BehDB,DUPLICATION_REF,1,
									   curids[0],curids[1],curids[2]);
					//Store duplicata
					addDuplicata(arg2id,arg2type);
				}
				break;

			case 'j': //Interleaved write
				//Checks semantic rules
				if(arg1type==TYPE_VAR && arg1id==curids[2] &&
					arg2type==TYPE_PERM && arg2id==curids[1]){
					//Progression toward the next node
					addNode(aut,'k',curids,curtypes);
					//printf("[+] Reach k by write\n");
				}
				break;
		}//swithch
	}//for
}



void parseDupDirectTransfer(long arg1id, int arg1type, long arg2id, int arg2type){
	char curnode;
	int final[3] = {0,0,0}; long lfinal[3] = {0,0,0};
	int i, nb = duplication.nbnautomata;

	for(i=0;i<nb;i++){
		struct PARSED_AUTOMATON * aut = &duplication.automata[i];
		//Test automata in the right state
		curnode = getCurrentState(aut);
		if(curnode!='s') continue;
		//Checks semantic rules
		if((arg1type==TYPE_THIS||isDuplicata(arg1id,arg1type))
			&& (arg2type==TYPE_PERM||arg2type==TYPE_BOOT||(arg2type==TYPE_THIS&&arg2id!=arg1id))){
			//Create a waiting new automaton replacing the parsed one
			startAutomaton(&duplication,'s',lfinal,final);
			nbambiguous++;
			aut = &duplication.automata[i]; //Update relocation
			//Progression toward the next node
			addNode(aut,'f',lfinal,final);
			printf("[+] Transfer duplication has been detected between %s%d and %s%d\n",
				typestr[arg2type],arg2id,typestr[arg1type],arg1id);
			storeBehaviorEntry(pf_BehDB,DUPLICATION_REF,0,arg1id,arg2id,0);
			//Store duplicata
			addDuplicata(arg2id,arg2type);

		}
	}
}

void parseDupAffect(long arg1id, int arg1type, long arg2id, int arg2type){
	char curnode;
	long curids[3]; int curtypes[3];
	int i, nb = duplication.nbnautomata;

	//Checks for duplicata affectation
	if(isDuplicata(arg2id,arg2type)){
		addDuplicata(arg1id,arg1type);
	}

	for(i=0;i<nb;i++){
		struct PARSED_AUTOMATON * aut = &duplication.automata[i];
		//Recover the state of the automaton
		curnode = getCurrentState(aut);
		getCurrentAttributes(aut,curids,curtypes);
		switch(curnode){
			
			case 'd': //Simple copy
			case 'm':
				//Checks semantic rules
				if(arg1type<=TYPE_VAR && arg2type==TYPE_VAR &&
					arg2id==curids[2]){
					//Start a parallel automaton with new variable
					curids[2] = arg1id;
					startAutomaton(&duplication,curnode,curids,curtypes);
					nbambiguous++;
					//printf("[+] Automaton divergence after affectation\n");
				}
				break;

			case 'j': //Interleaved copy
				//Checks semantic rules
				if(arg1type==TYPE_VAR && arg2type==TYPE_VAR &&
					arg2id==curids[2]){
					//Start a parallel automaton with new variable
					curids[2] = arg1id;
					startAutomaton(&duplication,'j',curids,curtypes);
					nbambiguous++;
					//printf("[+] Automaton divergence after affectation\n");
				}
				break;
		}//swithch
	}//for
}


void parseDupInterleavedBegin(){
	char curnode;
	long curids[3]; int curtypes[3];
	int i, nb = duplication.nbnautomata;

	for(i=0;i<nb;i++){
		struct PARSED_AUTOMATON * aut = &duplication.automata[i];
		//Recover the state of the automaton
		curnode = getCurrentState(aut);
		getCurrentAttributes(aut,curids,curtypes);
		if(curnode!='c') continue;
		//Progression toward the next node
		addNode(aut,'i',curids,curtypes);
		//printf("[+] Reach i\n");
	}//for
}


void parseDupInterleavedEnd(){
	char curnode;
	int final[3] = {0,0,0}; long lfinal[3] = {0,0,0};
	long curids[3]; int curtypes[3];
	int i, nb = duplication.nbnautomata;

	for(i=0;i<nb;i++){
		struct PARSED_AUTOMATON * aut = &duplication.automata[i];
		//Recover the state of the automaton
		curnode = getCurrentState(aut);
		getCurrentAttributes(aut,curids,curtypes);
		switch(curnode){
			
			case 'k': //End of interleaved copy
				//Progression toward the next node
				addNode(aut,'f',lfinal,final);
				printf("[+] Interleaved duplication has been detected "
					"between %s%d and %s%d through %s%d\n",typestr[curtypes[0]],
					curids[0],typestr[curtypes[1]],curids[1],typestr[curtypes[2]],curids[2]);
				storeBehaviorEntry(pf_BehDB,DUPLICATION_REF,2,
								   curids[0],curids[1],curids[2]);
				//Store duplicata
				if(curtypes[0]==TYPE_PERM){
					addDuplicata(curids[0],curtypes[0]);
				}
				if(curtypes[1]==TYPE_PERM){
					addDuplicata(curids[1],curtypes[1]);
				}
				break;

			case 'i': //Close loop
				//Return to state c 
				setCurrentState('c',aut);
				//printf("[+] Return to c\n");
				break;

			case 'j': //Close loop
				//Switch to state d
				setCurrentState('d',aut);
				break;
		}//swithch
	}//for
}


void updateDuplicationAutomata(unsigned long operation,
							   long arg1id, int arg1type,
							   long arg2id, int arg2type){
	switch(operation){
		case OP_CREA:
			parseDupCreate(arg1id,arg1type);
			break;
		case OP_OPEN:
			parseDupOpen(arg1id,arg1type);
			break;
		case OP_CLOS:
			parseDupClose(arg1id,arg1type);
			break;
		case OP_READ:
			parseDupRead(arg1id,arg1type,arg2id,arg2type);
			break;
		case OP_WRITE:
			parseDupDirectTransfer(arg1id,arg1type,arg2id,arg2type);
			parseDupWrite(arg1id,arg1type,arg2id,arg2type);
			break;
		case OP_AFF:
			parseDupAffect(arg1id,arg1type,arg2id,arg2type);
			break;
		case L_LOOP:
			parseDupInterleavedBegin();
			break;
		case R_LOOP:
			parseDupInterleavedEnd();
			break;
		//default:
			//ignore operation
	}

}


void addDuplicata(long objid, int objtype){
	if(isDuplicata(objid,objtype)) return;
	DuplicataIds[DuplicataNb] = objid;
	DuplicataTypes[DuplicataNb] = objtype;
	DuplicataNb++;
}

int isDuplicata(long objid, int objtype){
	int i;
	for(i=0;i<DuplicataNb;i++){
		//Checks if already existing
		if(DuplicataIds[i]==objid&&
			DuplicataTypes[i]==objtype) return 1;
	}
	return 0;
}



